#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/timer.h>

#include <asm/siginfo.h>
#include <linux/rcupdate.h>
#include <linux/sched.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>

#include "pwm.h"
#include "movement.h"
#include "buzzer.h"

#define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))

#define MOTOR0_PWM GPIO_TO_PIN(1, 18)
#define MOTOR0_REV GPIO_TO_PIN(1, 19)
#define MOTOR0_ENA GPIO_TO_PIN(1, 30)

#define MOTOR1_PWM GPIO_TO_PIN(0, 22)
#define MOTOR1_REV GPIO_TO_PIN(0, 23)
#define MOTOR1_ENA GPIO_TO_PIN(1, 2)

#define SERVO0 GPIO_TO_PIN(1, 0)

#define BUTTON0 GPIO_TO_PIN(0, 26)
#define BUZZER0 GPIO_TO_PIN(1, 1)

#define SIG_TEST 44	// we choose 44 as our signal number (real-time signals are in the range of 33 to 64)

MODULE_LICENSE("GPL");

static int dev_match(struct device*, struct device_driver *);

static struct bus_type msc_bus_type = {
	.name = "msc_bus",
	.match = &dev_match
};

static int dev_match(struct device *dev, struct device_driver *driver)
{
	return !strncmp(dev->bus->name, driver->bus->name, strlen(driver->bus->name));
}

static void bus_release(struct device *dev)
{
}

struct device msc_bus = {
	.init_name = "msc0",
	.release = &bus_release
};

///////////////////////////////// User Button //////////////////////////////

struct dentry *file;

static int pid;
static struct siginfo info;
static struct task_struct *t;

static ssize_t btn0_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	sprintf(buf, "%d\n", pid);
	return strlen(buf);
}

static ssize_t btn0_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	pid = 0;
	sscanf(buf, "%d", &pid);
	printk("pid = %d\n", pid);

	return count;
}

static struct device_attribute btn0_attr = {
	.attr = {
		.name = "pid",
		.mode = S_IRUGO | S_IWUGO                 // read/write
	},
	.show = &btn0_show,                               // read handler
	.store = &btn0_store                              // write handler
};

static void btn0_release_dev(struct device *dev)
{
	device_remove_file(dev->parent, &btn0_attr);
}

static struct device btn0_device = {
	.init_name = "button0",
	.parent = &msc_bus,
	.release = &btn0_release_dev
};

/////////////////////////// User Button Interrupt //////////////////////////

void start_user_task (unsigned long);
DECLARE_TASKLET (user_tasklet, start_user_task, 0);

irqreturn_t irq_handler(int irq, void *device_id)
{
	tasklet_schedule(&user_tasklet);
	return IRQ_HANDLED;
}

static struct timer_list btn_timer;	// Used for gpio soft debounce

void btn_timer_callback(unsigned long data)
{
	printk("Timer Finished.\n");
}

void start_user_task (unsigned long unused)
{
	if(pid == 0)
		return;

	setup_timer(&btn_timer, btn_timer_callback, 0);
	int ret = mod_timer(&btn_timer, jiffies + 1);

	if(ret)
		printk("Error in mod_timer\n");

	/* send the signal */
	memset(&info, 0, sizeof(struct siginfo));
	info.si_signo = SIG_TEST;
	info.si_code = SI_QUEUE;
				// this is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space,
        			// and kernel space should use SI_KERNEL. But if SI_KERNEL is used the real_time data 
        			// is not delivered to the user space signal handler function. 
	info.si_int = 1234;	//real time signals may have 32 bits of data.

	rcu_read_lock();
	t = get_pid_task(find_get_pid(pid), PIDTYPE_PID);  //find the task_struct associated with this pid

	if(t == NULL) {
		rcu_read_unlock();
		return;
	}

	rcu_read_unlock();
	ret = send_sig_info(SIG_TEST, &info, t);    //send the signal
	if (ret < 0) {
		printk("error sending signal\n");
	}
}

////////////////////////////////// Lifecycle //////////////////////////////

static struct dev
{
	int some_int;
}some_dev;

static struct servo_device *servo0;
static struct motor_device *motor0;
static struct motor_device *motor1;
static struct buzzer_device *buzzer0;

static int mod_init(void)
{
	if(gpio_is_valid(BUTTON0)) {
		gpio_request(BUTTON0, "User Button");
		gpio_direction_input(BUTTON0);

		irq_set_irq_type(gpio_to_irq(BUTTON0), IRQ_TYPE_EDGE_BOTH);
		request_irq(gpio_to_irq(BUTTON0), &irq_handler, 0, "User Button", &some_dev);
	}

	int ret = bus_register(&msc_bus_type);
	if(ret)
		return ret;

	ret = device_register(&msc_bus);
	if(ret) return ret;

	servo0 = servo_init(SERVO0, "servo0", &msc_bus);    // dynamic initialization if you want more add them.
                                                             // just make sure to release them when you are done.
	if((int)servo0 == -ENODEV || (int)servo0 == -ENOMEM) // still need to add exception for file creation error
		return (int)servo0;

	motor0 = motor_init(MOTOR0_PWM, MOTOR0_REV, MOTOR0_ENA, "motor0", &msc_bus);
	if((int)motor0 == -ENODEV || (int)motor0 == -ENOMEM)
		return (int)motor0;

	motor1 = motor_init(MOTOR1_PWM, MOTOR1_REV, MOTOR1_ENA, "motor1", &msc_bus);
	if((int)motor1 == -ENODEV || (int)motor1 == -ENOMEM)
		return (int)motor1;

	buzzer0 = buzzer_init(BUZZER0, "buzzer0", &msc_bus);
	if((int)buzzer0 == -ENODEV || (int)buzzer0 == -ENOMEM)
	return (int)buzzer0;

	ret = device_register(&btn0_device);
	if(ret) return -ENODEV;

	ret = device_create_file(&btn0_device, &btn0_attr);
	if(ret) return ret;

	return 0;
}

static void mod_exit(void)
{
	del_timer(&btn_timer);

	servo_release(servo0);
	motor_release(motor0);
	motor_release(motor1);
	buzzer_release(buzzer0);

	gpio_free(BUTTON0);
	free_irq(gpio_to_irq(BUTTON0), &some_dev);

	device_unregister(&btn0_device);
	device_unregister(&msc_bus);

	bus_unregister(&msc_bus_type);
}

module_init(mod_init);
module_exit(mod_exit);
